﻿using System;

namespace QAToolsAddin {

    class Uniform : Random {
        public double[,] GetDoubles(int nbValue) {
            var values = ExcelArray<double>.Create(nbValue, 1);
            int cpt = 1;
            while (cpt <= nbValue)
                values[cpt++, 1] = NextDouble();
            return values;
        }
    }

    class Beta : Uniform {
        private Gamma _gammaDistributionAlpha;
        private Gamma _gammaDistributionBeta;

        public Beta(double alpha, double beta) {
            _gammaDistributionAlpha = new Gamma(alpha, 1d);
            _gammaDistributionBeta = new Gamma(beta, 1d);
        }

        public override double NextDouble() {
            double x = _gammaDistributionAlpha.NextDouble();
            return x / (x + _gammaDistributionBeta.NextDouble());
        }
    }

    class Bernoulli : Uniform {
        private double _alpha;

        public Bernoulli(double alpha) {
            _alpha = alpha;
        }

        public override double NextDouble() {
            if (base.NextDouble() < _alpha)
                return 1d;
            else
                return 0d;
        }
    }

    class BetaPrime : Beta {
        public BetaPrime(double alpha, double beta)
            : base(alpha, beta) {
        }

        public override double NextDouble() {
            double betaVariate = base.NextDouble();
            return betaVariate / (1d - betaVariate);
        }
    }

    class Binomial : Uniform {
        private double _alpha;
        private double _beta;

        public Binomial(double alpha, double beta) {
            _alpha = alpha;
            _beta = beta;
        }

        public override double NextDouble() {
            double successes = 0d;
            for (int i = 0; i < _beta; i++) {
                if (base.NextDouble() < _alpha)
                    successes++;
            }
            return successes;
        }
    }

    class Cauchy : Uniform {
        private double _alpha;
        private double _gamma;

        public Cauchy(double alpha, double gamma) {
            _alpha = alpha;
            _gamma = gamma;
        }

        public override double NextDouble() {
            return _alpha + _gamma * Math.Tan(Math.PI * (base.NextDouble() - 0.5d));
        }
    }

    class Chi : Normal {
        private double _alpha;

        public Chi(double alpha)
            : base(0, 1d) {
            _alpha = alpha;
        }

        public override double NextDouble() {
            double sum = 0.0;
            for (int i = 0; i < _alpha; i++)
                sum += Math.Pow(base.NextDouble(), 2);
            return Math.Sqrt(sum);
        }
    }

    class ChiSquare : Normal {
        private double _alpha;

        public ChiSquare(double alpha)
            : base(0d, 1d) {
            _alpha = alpha;
        }

        public override double NextDouble() {
            double sum = 0d;
            for (int i = 0; i < _alpha; i++)
                sum += Math.Pow(base.NextDouble(), 2);
            return sum;
        }
    }

    class ContinuousUniform : Uniform {
        private double _alpha;
        private double _v1;

        public ContinuousUniform(double alpha, double beta) {
            _alpha = alpha;
            _v1 = beta - _alpha;
        }

        public override double NextDouble() {
            return _alpha + base.NextDouble() * _v1;
        }
    }

    class DiscreteUniform : Uniform {
        private double _alpha;
        private double _v1;

        public DiscreteUniform(double alpha, double beta) {
            _alpha = alpha;
            _v1 = beta + 1 - alpha;
        }

        public override double NextDouble() {
            return _alpha + base.Next() * _v1;
        }
    }

    class Erlang : Uniform {
        private double _scale;
        private double _shape;

        public Erlang(double scale, double shape) {
            _scale = scale;
            _shape = shape;
        }

        public override double NextDouble() {
            double prod = 1;
            for (int i = 1; i <= _scale; i++)
                prod = prod * base.NextDouble();
            return -_shape * Math.Log(prod);
        }
    }

    class Exponential : Uniform {
        private double _mean;

        public Exponential(double mean) {
            _mean = mean;
        }

        public override double NextDouble() {
            return -_mean * Math.Log(base.NextDouble());
        }
    }

    class Fisher : Uniform {
        private ChiSquare _chiSquareDistributionAlpha;
        private ChiSquare _chiSquareDistributionBeta;
        private double _v1;

        public Fisher(double alpha, double beta) {
            _chiSquareDistributionAlpha = new ChiSquare(alpha);
            _chiSquareDistributionBeta = new ChiSquare(beta);
            _v1 = beta / alpha;
        }

        public override double NextDouble() {
            return _chiSquareDistributionAlpha.NextDouble() / _chiSquareDistributionBeta.NextDouble() * _v1;
        }
    }


    class Gamma : Uniform {
        private double _shape;
        private double _scale;
        private Normal _normal;
        private Gamma _gamma;

        public Gamma(double shape, double scale) {
            _shape = shape;
            _scale = scale;
            _normal = new Normal();
            _gamma = new Gamma(shape + 1.0, 1.0);
        }

        public override double NextDouble() {
            double d, c, x, xsquared, v, u;
            if (_shape >= 1.0) {
                d = _shape - 1.0 / 3.0;
                c = 1.0 / Math.Sqrt(9.0 * d);
                for (; ; ) {
                    do {
                        x = _normal.NextDouble();
                        v = 1.0 + c * x;
                    } while (v <= 0.0);
                    v = v * v * v;
                    u = base.NextDouble();
                    xsquared = x * x;
                    if (u < 1.0 - .0331 * xsquared * xsquared || Math.Log(u) < 0.5 * xsquared + d * (1.0 - v + Math.Log(v)))
                        return _scale * d * v;
                }
            } else if (_shape <= 0.0) {
                throw new ArgumentOutOfRangeException(string.Format("Shape must be positive. Received {0}.", _shape));
            } else {
                double g = _gamma.NextDouble();
                double w = base.NextDouble();
                return _scale * g * Math.Pow(w, 1.0 / _shape);
            }
        }

    }

    /*
        class Gamma : Distribution
        {
            private double _alpha;
            private double _theta;
            private double _v1;
            private double _v2;

            public Gamma(double alpha, double theta) {
                _alpha = alpha;
                _theta = theta;
                _v1 = alpha - Math.Floor(alpha);
                _v2 = Math.E / (Math.E + _v1);
            }

            public override double NextDouble() {
                double xi, eta, gen1, gen2;
                do {
                    gen1 = 1d - base.NextDouble();
                    gen2 = 1d - base.NextDouble();
                    if (gen1 <= _v2) {
                        xi = Math.Pow(gen1 / _v2, 1d / _v1);
                        eta = gen2 * Math.Pow(xi, _v1 - 1d);
                    } else {
                        xi = 1d - Math.Log((gen1 - _v2) / (1d - _v2));
                        eta = gen2 * Math.Pow(Math.E, -xi);
                    }
                } while (eta > Math.Pow(xi, _v1 - 1d) * Math.Pow(Math.E, -xi));

                for (int i = 1; i <= _alpha; i++)
                    xi -= Math.Log(base.NextDouble());

                return xi * _theta;
            }
        }
    */
    class Geometric : Uniform {
        private double _probability;

        public Geometric(double probability) {
            _probability = probability;
        }

        public override double NextDouble() {
            return (int)(Math.Log(base.NextDouble()) / Math.Log(1d - _probability) - 1d);
        }
    }

    class Laplace : Uniform {
        private double _alpha;
        private double _mu;

        public Laplace(double alpha, double mu) {
            _alpha = alpha;
            _mu = mu;
        }

        public override double NextDouble() {
            double rand = 0.5d - base.NextDouble();
            return _mu - _alpha * Math.Sign(rand) * Math.Log(2.0d * Math.Abs(rand));
        }
    }

    class Lognormal : Normal {
        private double _mu;
        private double _sigma;

        public Lognormal(double mu, double sigma)
            : base(0d, 1d) {
            _mu = mu;
            _sigma = sigma;
        }

        public override double NextDouble() {
            return Math.Exp(base.NextDouble() * _sigma + _mu);
        }
    }

    class Normal : Uniform {
        private double _mean = 0;
        private double _std = 0;
        private double _2PI = 2.0 * Math.PI;

        public Normal(double mean, double std) {
            _mean = mean;
            _std = std;
        }

        public Normal() { }

        public override double NextDouble() {
            double u1 = base.NextDouble();
            double u2 = base.NextDouble();
            if (_std == 0)
                return Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(_2PI * u2);
            else
                return _mean + _std * Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(_2PI * u2);
        }
    }

    class Pareto : Uniform {
        private double _alpha;
        private double _v1;

        public Pareto(double alpha, double beta) {
            _alpha = alpha;
            _v1 = 1d / beta;
        }

        public override double NextDouble() {
            return _alpha / Math.Pow(1d - base.NextDouble(), _v1);
        }
    }

    class Poisson : Uniform {
        private double _v1;

        public Poisson(double lambda) {
            _v1 = Math.Exp(-1.0 * lambda);
        }

        public override double NextDouble() {
            double count = 0d;
            for (double product = base.NextDouble(); product >= _v1; product *= base.NextDouble())
                count++;
            return count;
        }
    }

    class Power : Uniform {
        private double _beta;
        private double _v1;

        public Power(double alpha, double beta) {
            _beta = beta;
            _v1 = 1.0d / alpha;
        }

        public override double NextDouble() {
            return Math.Pow(base.NextDouble(), _v1) / _beta;
        }
    }

    class Rayleigh : Uniform {
        private Normal _normalDistribution1;
        private Normal _normalDistribution2;

        public Rayleigh(double sigma) {
            _normalDistribution1 = new Normal(0d, sigma);
            _normalDistribution2 = new Normal(0d, sigma);
        }

        public override double NextDouble() {
            return Math.Sqrt(Math.Pow(_normalDistribution1.NextDouble(), 2) + Math.Pow(_normalDistribution2.NextDouble(), 2));
        }
    }

    class Students : Uniform {
        private double _nu;

        private Normal _normalDistribution;
        private ChiSquare _chiSquareDistribution;

        public Students(double nu) {
            _nu = nu;
            _normalDistribution = new Normal(0d, 1d);
            _chiSquareDistribution = new ChiSquare(_nu);
        }

        public override double NextDouble() {
            return _normalDistribution.NextDouble() / Math.Sqrt(_chiSquareDistribution.NextDouble() / _nu);
        }
    }

    class Triangular : Uniform {
        private double _alpha;
        private double _beta;
        private double _gamma;
        private double _v1;
        private double _v2;
        private double _v3;
        private double _v4;

        public Triangular(double alpha, double beta, double gamma) {
            _alpha = alpha;
            _beta = beta;
            _gamma = gamma;
            _v1 = _gamma - _alpha;
            _v2 = _beta - _alpha;
            _v3 = Math.Sqrt(_v1 * _v2);
            _v4 = Math.Sqrt(_beta - _gamma);
        }

        public override double NextDouble() {
            double genNum = base.NextDouble();
            if (genNum <= _v1 / _v2)
                return _alpha + Math.Sqrt(genNum) * _v3;
            else
                return _beta - Math.Sqrt(genNum * _v2 - _v1) * _v4;
        }
    }

    class Weibull : Uniform {
        private double _shape;
        private double _scale;

        public Weibull(double shape, double scale) {
            _shape = shape;
            _scale = scale;
            if (shape <= 0.0 || scale <= 0.0)
                throw new ArgumentOutOfRangeException("Shape and scale parameters must be positive");
        }

        public override double NextDouble() {
            return _scale * Math.Pow(-Math.Log(base.NextDouble()), 1d / _shape);
        }
    }

}
